package com.goudan.demo.util;

import com.goudan.demo.base.dao.SequenceDao;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.concurrent.atomic.AtomicLong;

public class SequenceUtil {
    private static final HashMap<String, SequenceUtil.SequenceInfo> currSeqMap = new HashMap();
    private static String ID_COMMON = "ID_COMMON";

    public SequenceUtil() {
    }

    public static String getId(UniqueIdCode idCode) {
        StringBuffer strBuff = new StringBuffer(19);
        if (StringUtils.isBlank(idCode.getCode()) || 2 != idCode.getCode().length() && 3 != idCode.getCode().length()) {
            throw new IllegalArgumentException("uniqueIdCode format error");
        } else {
            if (2 == idCode.getCode().length()) {
                strBuff.append("C").append(idCode.getCode());
            } else {
                strBuff.append(idCode.getCode());
            }

            strBuff.append(DateUtil.getCurDate(DateUtil.DATE_FROMAT_YYMMDD));
            String sequenceName = idCode.getSequenceName();
            if (StringUtils.isBlank(sequenceName)) {
                sequenceName = ID_COMMON;
            }

            long value = getNextValue(sequenceName, 4);
            strBuff.append(StringFillUtil.LeftFill(String.valueOf(value), '0', 10));
            return strBuff.toString();
        }
    }

    protected static long getNextValue(String idType, int size) {
        long intPow = (long) Math.pow(10.0D, (double) size);
        SequenceUtil.SequenceInfo sequenceInfo = currSeqMap.get(idType);
        if (sequenceInfo == null) {
            synchronized (currSeqMap) {
                sequenceInfo = currSeqMap.get(idType);
                if (sequenceInfo == null) {
                    sequenceInfo = new SequenceUtil.SequenceInfo();
                    currSeqMap.put(idType, sequenceInfo);
                }
            }
        }
        long seqValue = sequenceInfo.seqNo.incrementAndGet();
        long oldDbSeq = sequenceInfo.dbSeq;

        while (seqValue >= intPow || oldDbSeq <= 0L) {
            synchronized (sequenceInfo) {
                if (oldDbSeq == sequenceInfo.dbSeq) {
                    sequenceInfo.dbSeq = getDbSeqValue(idType);
                    seqValue = 1L;
                    sequenceInfo.seqNo.set(seqValue);
                } else {
                    seqValue = sequenceInfo.seqNo.incrementAndGet();
                }

                oldDbSeq = sequenceInfo.dbSeq;
            }
        }

        return oldDbSeq * intPow + seqValue;
    }

    private static long getDbSeqValue(String idType) {
        SequenceDao sequenceDao = SpringContextHolder.getBean("sequenceDao");
        return sequenceDao.getSequenceValue(idType);
    }

    static class SequenceInfo {
        long dbSeq = -1L;
        AtomicLong seqNo = new AtomicLong(-1L);

        SequenceInfo() {
        }
    }

}
